using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;

using Atomic.Libraries.Mathematics;
using Atomic.Libraries.Plotting.SpacePlotting;
using Atomic.Structures.Symmetries;

namespace Atomic.Structures
{
	[Serializable]
	[DebuggerDisplay("{DebuggerDisplay}")]
	public class BodyCenteredCubicLattice : Lattice
	{
		public BodyCenteredCubicLattice(double a)
			: this(Basis.StandardBasis, a, SpaceMatrix.Identity)
		{
		}

		public BodyCenteredCubicLattice(Basis basis, double a, SpaceMatrix transform)
			: this(basis.Decompose(new Position(transform * new SpaceVector(-0.5, 0.5, 0.5))) * a, basis.Decompose(new Position(transform * new SpaceVector(0.5, -0.5, 0.5))) * a, basis.Decompose(new Position(transform * new SpaceVector(0.5, 0.5, -0.5))) * a, a, transform)
		{
		}

		private BodyCenteredCubicLattice(Position a, Position b, Position c, double a0, SpaceMatrix transform)
			: base(a, b, c)
		{
			if (a0 <= 0.0)
			{
				throw new ArgumentException();
			}

			CheckOrthogonal(transform);

			A0 = a0;
			Transform = transform;

			Gamma = new Position(this, SymmetryPoints.Gamma);
			X = new Position(this, SymmetryPoints.X);
			W = new Position(this, SymmetryPoints.W);
			K = new Position(this, SymmetryPoints.K);
			U = new Position(this, SymmetryPoints.U);
			L = new Position(this, SymmetryPoints.L);
			Xp = new Position(this, SymmetryPoints.Xp);
		}

		public static bool TryParse(Basis basis, SpaceVector a, SpaceVector b, SpaceVector c, out Lattice lattice)
		{
			lattice = null;
			return false;
		}

		protected override Lattice CreateReciprocalLattice()
		{
			// http://math.stackexchange.com/a/61495
			return new FaceCenteredCubicLattice(ParentBasis, 4.0 * Math.PI / A0, Transform);
		}

		public IEnumerable<ISpacePlot> PlotWignerSeitzCell()
		{
			throw new NotImplementedException();
			/*// Point marking two of the boundary lines of the Wigner-Seitz cell. By symmetry this is sufficient to draw the entire cell.
			Position p0 = new Position(this, W.Coordinates);
			Position p1 = new Position(this, W.Coordinates + 2.0 * (K.Coordinates - W.Coordinates));
			Position p2 = new Position(this, W.Coordinates + 2.0 * (U.Coordinates - W.Coordinates));

			// Needs an atom a the origin to determine symmetries; so just add an arbitrary atom.
			foreach (SpaceGroupSymmetryOperation o in new Structure(this).Add(Atom.Vacancy, SpaceVector.Zero).SpaceGroup.SymmetryOperations)
			{
				Position q0 = o.Transform(p0);
				Position q1 = o.Transform(p1);
				Position q2 = o.Transform(p2);
				yield return new SpaceLine(q0.PhysicalCoordinates, q1.PhysicalCoordinates);
				yield return new SpaceLine(q0.PhysicalCoordinates, q2.PhysicalCoordinates);
			}

			// Plot symmetry lines (i.e. boundary of the IBZ).
			yield return new SpaceLine(Gamma.PhysicalCoordinates, L.PhysicalCoordinates );
			yield return new SpaceLine(Gamma.PhysicalCoordinates, K.PhysicalCoordinates);
			yield return new SpaceLine(Gamma.PhysicalCoordinates, X.PhysicalCoordinates);
			yield return new SpaceLine(X.PhysicalCoordinates, W.PhysicalCoordinates);
			yield return new SpaceLine(K.PhysicalCoordinates, W.PhysicalCoordinates);
			yield return new SpaceLine(L.PhysicalCoordinates, W.PhysicalCoordinates);
			yield return new SpaceLine(L.PhysicalCoordinates, U.PhysicalCoordinates);
			yield return new SpaceLine(X.PhysicalCoordinates, U.PhysicalCoordinates);
			yield return new SpaceLine(W.PhysicalCoordinates, U.PhysicalCoordinates);
			yield return new SpaceLine(K.PhysicalCoordinates, L.PhysicalCoordinates);*/
		}

		/// <summary>
		/// Lattice constant.
		/// </summary>
		public double A0
		{
			get;
			private set;
		}

		/// <summary>
		/// The orthogonal transform matrix from the standard lattice vector representation.
		/// </summary>
		public SpaceMatrix Transform
		{
			get;
			private set;
		}

		public Position Gamma
		{
			get;
			private set;
		}

		public Position X
		{
			get;
			private set;
		}

		public Position W
		{
			get;
			private set;
		}

		public Position K
		{
			get;
			private set;
		}

		public Position U
		{
			get;
			private set;
		}

		public Position L
		{
			get;
			private set;
		}

		public Position Xp
		{
			get;
			private set;
		}

		private string DebuggerDisplay
		{
			get
			{
				return string.Format(CultureInfo.InvariantCulture, "bcc, A = ({0:f4}, {1:f4}, {2:f4}), B = ({3:f4}, {4:f4}, {5:f4}), C = ({6:f4}, {7:f4}, {8:f4})",
					A.PhysicalCoordinates.X, A.PhysicalCoordinates.Y, A.PhysicalCoordinates.Z, 
					B.PhysicalCoordinates.X, B.PhysicalCoordinates.Y, B.PhysicalCoordinates.Z, 
					C.PhysicalCoordinates.X, C.PhysicalCoordinates.Y, C.PhysicalCoordinates.Z);
			}
		}

		/// <summary>
		/// High-symmetry points of the lattice. If the lattice is known to be bcc (and the type is BodyCenteredCubicLattice),
		/// then these relative lattice positions are correct.
		/// </summary>
		public static class SymmetryPoints
		{
			static SymmetryPoints()
			{
				Gamma = new SpaceVector(0.0, 0.0, 0.0);
				X = new SpaceVector(0.5, 0.0, 0.5);
				W = new SpaceVector(0.5, 0.25, 0.75);
				K = new SpaceVector(0.375, 0.375, 0.75);
				U = new SpaceVector(0.625, 0.25, 0.625);
				L = new SpaceVector(0.5, 0.5, 0.5);

				// Point X' outside the Wigner-Seitz cell, but useful for dispersion curves.
				//Xp = 2.0 * W - X;
				Xp = W + (W - X);
			}

			public static SpaceVector Gamma
			{
				get;
				private set;
			}

			public static SpaceVector X
			{
				get;
				private set;
			}

			public static SpaceVector W
			{
				get;
				private set;
			}

			public static SpaceVector K
			{
				get;
				private set;
			}

			public static SpaceVector U
			{
				get;
				private set;
			}

			public static SpaceVector L
			{
				get;
				private set;
			}

			public static SpaceVector Xp
			{
				get;
				private set;
			}
		}
	}
}
